package com.sina.finance.api.framework.permission.filter;

import com.sina.finance.api.framework.constants.ErrorCodeConstants;
import com.sina.finance.api.framework.permission.Constants;
import com.sina.finance.api.framework.permission.exception.NoSecretException;
import com.sina.finance.api.framework.permission.exception.SignCheckException;
import com.sina.finance.api.framework.permission.realm.StatelessToken;
import com.sina.finance.api.framework.permission.timestamp.OptionalWebTimeStampChecker;
import com.sina.finance.api.framework.permission.timestamp.SimpleTimeStampChecker;
import com.sina.finance.api.framework.response.ErrorResponse;
import com.sina.finance.api.framework.service.IErrorResponseService;
import com.sina.finance.api.framework.utils.ModelAndViewUtils;
import com.sina.finance.api.framework.utils.ResponseUtils;
import com.sina.finance.api.framework.utils.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.beans.factory.InitializingBean;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;


public class StatelessAuthcFilter extends AccessControlFilter implements InitializingBean {

    public static final String API_CHECK_TIMESTAMP_MAX_EXPIRE = "api.check.timestamp.max.expire";
    @Resource
    protected IErrorResponseService errorResponseService;

    @Resource
    private Properties configProperties;
    OptionalWebTimeStampChecker optionalWebTimeStampChecker;


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //1、客户端生成的消息摘要
        String clientDigest = request.getParameter(Constants.PARAM_DIGEST);


        //2、客户端传入的用户身份
        String key = request.getParameter(Constants.PARAM_KEY);

        //3、客户端请求的参数列表
        Map<String, String[]> params = new HashMap<String, String[]>(request.getParameterMap());
        params.remove(Constants.PARAM_DIGEST);

        // 如果没有传递format
        String format = request.getParameter(Constants.SYS_PARAM_FORMAT).toUpperCase();

        String testMode = configProperties.getProperty("api.mode.test");

        String partnerCode = request.getParameter(Constants.PARTNER_CODE);

        String method = request.getParameter(Constants.SYS_PARAM_METHOD);




        if (!Boolean.valueOf(testMode)) {

            if (StringUtils.isEmpty(format)) {
                String responseCode = ErrorCodeConstants.SYS_ERROR_CODE_S5;
                format = "JSON";
                return responseError((HttpServletRequest) request, response, format, responseCode);
            }

            if (params.isEmpty()) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S1);
            }

            if (key == null) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S2);
            }

            if (clientDigest == null) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S7);
            }


            if (partnerCode == null) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S14);
            }

            if(StringUtils.isEmpty(method)){
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S4);
            }

            //时间戳校验
            String timeStamp = request.getParameter(Constants.SYS_PARAM_TIMESTAMP);;
            if(StringUtils.isEmpty(timeStamp)) {
                return this.responseError((HttpServletRequest)request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S8);
            }

            if(!optionalWebTimeStampChecker.checkTimeStamp(params, method)) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S17);
            }

            StatelessToken token = new StatelessToken(key, partnerCode, params, clientDigest);



            try {
                Subject subject = getSubject(request, response);
                subject.login(token);
                //subject.checkPermission(method);
            } catch (SignCheckException e) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S11);
            } catch (UnknownAccountException e) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S3);
            } catch (NoSecretException e) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S18);
            } catch (AuthenticationException e) {
                return responseError((HttpServletRequest) request, response, format, ErrorCodeConstants.SYS_ERROR_CODE_S14);
            }
        }

        return true;
    }

    private boolean responseError(HttpServletRequest request, ServletResponse response, String format, String responseCode) throws Exception {
        Map<String, ErrorResponse> model = new HashMap<>();
        ErrorResponse errorResponse = errorResponseService.getErrorResponse(responseCode);
        model.put("errorResponse", errorResponse);
        ModelAndViewUtils.getMarshallingView("error_response", format, response.getClass()).render(model, request, (HttpServletResponse) response);
        return false;
    }

    //登录失败时默认返回401状态码
    private void onLoginFail(ServletResponse response) throws IOException {
        String contentType = "text/plain";
        int scUnauthorized = HttpServletResponse.SC_UNAUTHORIZED;
        String content = "login error";
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        ResponseUtils.writeToResponse(contentType, scUnauthorized, content, httpResponse);
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        this.optionalWebTimeStampChecker = new OptionalWebTimeStampChecker(
                new SimpleTimeStampChecker(Integer.parseInt(configProperties.getProperty(API_CHECK_TIMESTAMP_MAX_EXPIRE)) * 1000),
                "timestamp",
                configProperties,
                "api.check.timestamp.optional.method"
        );
    }
}
